/**
 * 2025/01/01 - 2025/12/31
 */
package com.snail.starter.db.holder;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;

/**
 * @author zgadmin
 * @date 2025/8/1 16:27
 */
@Slf4j
@Component
public class DynamicTransactionTemplate {

    @Resource
    TransactionTemplate transactionTemplate;

    /**
     * 当前数据源下进行事务操作,有异常
     *
     * @param transactionAction 数据库操作
     * @param <T>               返回值类型
     * @return 需要返回的数据
     */
    public <T> T doTransaction(DsTransactionCallback<T> transactionAction) {
        return DynamicDataSourceTemplate.callback(() -> transactionTemplate.execute(transactionAction));
    }

    /**
     * 指定数据源下进行事务操作,有异常
     *
     * @param dataSourceId      数据源ID
     * @param transactionAction 数据库操作
     * @param <T>               返回值类型
     * @return 需要返回的数据
     */
    public <T> T doTransaction(String dataSourceId, DsTransactionCallback<T> transactionAction) {
        return DynamicDataSourceTemplate.callback(dataSourceId, () -> transactionTemplate.execute(transactionAction));
    }

    /**
     * 当前数据源下进行事务操作,无异常
     *
     * @param transactionAction 数据库操作
     * @param <T>               返回值类型
     * @return 需要返回的数据
     */
    public <T> T doTransactionWithThrowable(DsTransactionCallback<T> transactionAction) {
        return DynamicDataSourceTemplate.callbackWithThrowable(() -> transactionTemplate.execute(transactionAction));
    }

    /**
     * 指定数据源下进行事务操作,无异常
     *
     * @param dataSourceId      数据源ID
     * @param transactionAction 数据库操作
     * @param <T>               返回值类型
     * @return 需要返回的数据
     */
    public <T> T doTransactionWithThrowable(String dataSourceId, DsTransactionCallback<T> transactionAction) {
        return DynamicDataSourceTemplate.callbackWithThrowable(dataSourceId, () -> transactionTemplate.execute(transactionAction));
    }

    /**
     * 当前数据源下进行事务操作,无异常
     *
     * @param transactionWithoutResultFunction 数据库操作
     */
    public void doTransaction(DsTransactionWithoutResultFunction transactionWithoutResultFunction) {
        DynamicDataSourceTemplate.execute(
                () -> transactionTemplate.executeWithoutResult(status -> template(status, transactionWithoutResultFunction)));
    }

    /**
     * 指定数据源下进行事务操作,无异常
     *
     * @param dataSourceId                     数据源ID
     * @param transactionWithoutResultFunction 数据库操作
     */
    public void doTransaction(String dataSourceId, DsTransactionWithoutResultFunction transactionWithoutResultFunction) {
        DynamicDataSourceTemplate.execute(dataSourceId,
                () -> transactionTemplate.executeWithoutResult(status -> template(status, transactionWithoutResultFunction)));
    }

    /**
     * 当前数据源下进行事务操作,有异常
     *
     * @param transactionWithoutResultFunction 数据库操作
     */
    public void doTransactionWithThrowable(DsTransactionWithoutResultFunction transactionWithoutResultFunction) {
        DynamicDataSourceTemplate.executeWithThrowable(
                () -> transactionTemplate.executeWithoutResult(status -> templateWithThrowable(status, transactionWithoutResultFunction)));
    }

    /**
     * 指定数据源下进行事务操作,有异常
     *
     * @param dataSourceId                     数据源ID
     * @param transactionWithoutResultFunction 数据库操作
     */
    public void doTransactionWithThrowable(String dataSourceId, DsTransactionWithoutResultFunction transactionWithoutResultFunction) {
        DynamicDataSourceTemplate.executeWithThrowable(dataSourceId,
                () -> transactionTemplate.executeWithoutResult(status -> templateWithThrowable(status, transactionWithoutResultFunction)));
    }

    private static void template(TransactionStatus transactionStatus, DsTransactionWithoutResultFunction transactionWithoutResultFunction) {
        try {
            templateWithThrowable(transactionStatus, transactionWithoutResultFunction);
        } catch (Throwable throwable) {
            transactionStatus.setRollbackOnly();
        }
    }

    private static void templateWithThrowable(TransactionStatus transactionStatus, DsTransactionWithoutResultFunction transactionWithoutResultFunction) {
        try {
            transactionWithoutResultFunction.doTransactionWithoutResult();
        } catch (Throwable throwable) {
            transactionStatus.setRollbackOnly();
            log.warn("", throwable);
            log.warn("template without result rollback");
            throw throwable;
        }
    }


    public interface DsTransactionCallback<T> extends TransactionCallback<T> {

    }

    public interface DsTransactionWithoutResultFunction {

        void doTransactionWithoutResult();

    }

    public interface DsTransactionFunction<T> {

        T doTransaction();

    }

}